#include "stdafx.h"
#include "ServerBroadcaster.h"

#include <list>

#include "..\Common\CommonUtil.h"


broadcaster::broadcaster(void)
: m_pServer(NULL), m_pClientManager(NULL) {	
	InitializeCriticalSection(&m_CriticalSection);

	m_SendEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
}

broadcaster::~broadcaster(void) {
	DeleteCriticalSection(&m_CriticalSection);

	CloseHandle(m_SendEvent);
}

void broadcaster::LinkServer(server_base* pServer) {
	assert(m_pServer == NULL);
	assert(pServer != NULL);

	m_pServer = pServer;
}

void broadcaster::LinkClient(client_manager* pClientManager) {
	assert(m_pClientManager == NULL);
	assert(pClientManager != NULL);

	m_pClientManager = pClientManager;
}

void broadcaster::PopMessage(void) {
	assert(!m_MessageDeque.empty());

	cs_auto cs(&m_CriticalSection);
	
	if(m_MessageDeque.front().first == B_NEW_OBJECT) 
		delete [] ((broadcast_msg_NEW_OBJECT*)m_MessageDeque.front().second)->ExtraPtr;
	if(m_MessageDeque.front().first == B_MESSAGE) 
		delete [] ((broadcast_msg_MESSAGE*)m_MessageDeque.front().second)->MsgString;
	// add code point. at new brodcast message. (if new message have extra date. )

	if(m_MessageDeque.front().second != NULL)
		delete m_MessageDeque.front().second;

	m_MessageDeque.pop_front();	

	if(m_MessageDeque.empty()) 
		ResetEvent(m_SendEvent);
}

void broadcaster::PushMessageNewClient(const ip& IP, const unsigned int& ClientCode, const unsigned int& UdpPort) {
	cs_auto cs(&m_CriticalSection);
	
	broadcast_msg_NEW_CLIENT* PushData = new broadcast_msg_NEW_CLIENT;
	PushData->IP			= IP;
	PushData->ClientCode	= ClientCode;
	PushData->UdpPort		= UdpPort;	

	m_MessageDeque.push_back(make_pair(B_NEW_CLIENT, (broadcast_msg_base*)PushData));
	SetEvent(m_SendEvent);
}

void broadcaster::PushMessageOffClient(const unsigned int& ClientCode) {
	cs_auto cs(&m_CriticalSection);
	
	broadcast_msg_OFF_CLIENT* PushData = new broadcast_msg_OFF_CLIENT;
	PushData->ClientCode = ClientCode;	

	m_MessageDeque.push_back(make_pair(B_OFF_CLIENT, (broadcast_msg_base*)PushData));
	SetEvent(m_SendEvent);
}

void broadcaster::PushMessageNewObject(const unsigned int& ObjectID, const OBJECT_TYPE& ObjectType, const unsigned int& OwnerCode, 
									   const unsigned int& ExtraLength, char* ExtraPtr) {
	cs_auto cs(&m_CriticalSection);

	broadcast_msg_NEW_OBJECT* PushData = new broadcast_msg_NEW_OBJECT;
	PushData->ObjectID		= ObjectID;
	PushData->ObjectType	= ObjectType;
	PushData->OwnerCode		= OwnerCode;
	PushData->ExtraLength	= ExtraLength;
	PushData->ExtraPtr		= new char[ExtraLength];
								memcpy(PushData->ExtraPtr, ExtraPtr, ExtraLength);

	m_MessageDeque.push_back(make_pair(B_NEW_OBJECT, (broadcast_msg_base*)PushData));
	SetEvent(m_SendEvent);
}

void broadcaster::PushMessageDelObject(const unsigned int& ObjectID) {
	cs_auto cs(&m_CriticalSection);

	broadcast_msg_DEL_OBJECT* PushData = new broadcast_msg_DEL_OBJECT;
	PushData->ObjectID = ObjectID;

	m_MessageDeque.push_back(make_pair(B_DEL_OBJECT, (broadcast_msg_base*)PushData));
	SetEvent(m_SendEvent);
}

void broadcaster::PushMessageMessage(const unsigned int& MsgLength, char* MsgString) {
	cs_auto cs(&m_CriticalSection);

	broadcast_msg_MESSAGE* PushData = new broadcast_msg_MESSAGE;
	PushData->MsgLength = MsgLength;
	PushData->MsgString = new char[MsgLength];
							memcpy(PushData->MsgString, MsgString, MsgLength);

	m_MessageDeque.push_back(make_pair(B_MESSAGE, (broadcast_msg_base*)PushData));
	SetEvent(m_SendEvent);
}

void broadcaster::PushMessageCheckSocket(void) {
	cs_auto cs(&m_CriticalSection);

	broadcast_msg_CHECK_SOCKET* PushData = new broadcast_msg_CHECK_SOCKET;
	m_MessageDeque.push_back(make_pair(B_CHECK_SOCKET, (broadcast_msg_base*)PushData));
	SetEvent(m_SendEvent);
}

void broadcaster::PushMessageMissileCrush(const unsigned int& MissileOwner, const unsigned int& MissileID, const unsigned int& TargetClient) {
	cs_auto cs(&m_CriticalSection);

	broadcast_msg_MISSILE_CRUSH* PushData = new broadcast_msg_MISSILE_CRUSH;
	PushData->MissileOwner	= MissileOwner;
	PushData->MissileId		= MissileID;
	PushData->TargetClient  = TargetClient;
	m_MessageDeque.push_back(make_pair(B_MISSILE_CRUSH, (broadcast_msg_base*)PushData));
	SetEvent(m_SendEvent);
}

void broadcaster::Send(void) {
	std::vector<unsigned int> AccessOffClient;

	WaitForSingleObject(m_SendEvent, INFINITE);

	EnterCriticalSection(&m_CriticalSection);	

	int DataLength = 0;
	switch(m_MessageDeque.front().first)  {
		case B_NEW_CLIENT:
			DataLength = sizeof(broadcast_msg_NEW_CLIENT);
			break;
		case B_OFF_CLIENT:
			DataLength = sizeof(broadcast_msg_OFF_CLIENT);
			break;
		case B_NEW_OBJECT:
			DataLength = sizeof(broadcast_msg_NEW_OBJECT);
			break;
		case B_DEL_OBJECT:
			DataLength = sizeof(broadcast_msg_DEL_OBJECT);
			break;
		case B_MESSAGE:
			DataLength = sizeof(broadcast_msg_MESSAGE);
			break;
		case B_CHECK_SOCKET:
			DataLength = sizeof(broadcast_msg_CHECK_SOCKET);
			break;
		case B_MISSILE_CRUSH:
			DataLength = sizeof(broadcast_msg_MISSILE_CRUSH);
		// add code point. at new brodcast message.
	}

	for(m_pClientManager->StartRepeat();
		m_pClientManager->Repeat() != NULL;
		m_pClientManager->NextRepeat() ) {			
			// send broadcast code.
			if(m_pClientManager->Repeat()->BroadcastNetwork->
							Send((char*)(&(m_MessageDeque.front().first)), sizeof(BRODCAST_MESSAGE)) == false) {
				AccessOffClient.push_back(m_pClientManager->Repeat()->ClientCode);
				continue;
			}

			// send info data.
			if(DataLength != 0) 
				if(m_pClientManager->Repeat()->BroadcastNetwork->Send((char*)(m_MessageDeque.front().second), DataLength) == false) {
					AccessOffClient.push_back(m_pClientManager->Repeat()->ClientCode);
					continue;
				}
			
			//send extra data.
			if(m_MessageDeque.front().first == B_NEW_OBJECT) 
				if(m_pClientManager->Repeat()->BroadcastNetwork->Send(
								(char*)(((broadcast_msg_NEW_OBJECT*)m_MessageDeque.front().second)->ExtraPtr), 
								((broadcast_msg_NEW_OBJECT*)m_MessageDeque.front().second)->ExtraLength) == false)  {
					AccessOffClient.push_back(m_pClientManager->Repeat()->ClientCode);
					continue;
				}
			if(m_MessageDeque.front().first == B_MESSAGE) 
				if(m_pClientManager->Repeat()->BroadcastNetwork->Send(
								(char*)(((broadcast_msg_MESSAGE*)m_MessageDeque.front().second)->MsgString), 
								((broadcast_msg_MESSAGE*)m_MessageDeque.front().second)->MsgLength) == false) {
					AccessOffClient.push_back(m_pClientManager->Repeat()->ClientCode);
					continue;
				}
			// add code point. if new broadcast code have extra data.
	}
	m_pClientManager->EndRepeat();	

	LeaveCriticalSection(&m_CriticalSection);	

	PopMessage();

	// some client's broadcast is off. delete client. 
	for(int i = 0; i < AccessOffClient.size(); ++i)
		this->m_pServer->DeleteClient(AccessOffClient[i]);

}